/*
 * Login.java
 *
 * Created on 26 februari 2008, 22:05
 */
package gui;

import data.*;
import data.logic.XmlServer;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import gui.wizard.*;
import java.awt.*;
import java.awt.event.FocusListener;
import java.awt.event.KeyListener;
import java.io.File;
import javax.swing.border.EmptyBorder;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import maiaclient.Maia;
import org.xml.sax.SAXException;

/**
 *
 * @author groep 11
 * @date 2007-2008
 */
public class Login extends javax.swing.JFrame {

    // Final variables
    private final String IMAGEICONLOC = "/img/logo.gif";
    private final String LOGINLOGOLOC = "/img/maiaLogoLogin.jpg";
    private final String XMLWERELDENLOC = "http://www.vdbossche.com/MaiaWerelden.xml";
    private final String XMLREMEMBERME = "src/data/xml/remember.xml";
    private final String LOGINTITLE = "Maia - Login";
    private final int LOGINFRAMEWIDTH = 450;
    private final int LOGINFRAMEHEIGHT = 350;
    // Component Fields
    private JComboBox cboWorld;
    private JTextField txtUser;
    private JPasswordField pwfPassword;
    private JCheckBox chbRememberMe;
    private JButton btnLogin;
    private JButton btnRegister;
    private WizardPanelDescriptor userDataDspt;
    private WizardPanelDescriptor maiaDataDspt;

    /** 
     * Constructor wich sets all the Frame options and loads the components.
     * It also checks if we have to load xmlfile for remember function. 
     */
    public Login() {

        setPreferredSize(new Dimension(LOGINFRAMEWIDTH, LOGINFRAMEHEIGHT));
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        initComponents();
        initMainComponents();

        setTitle(LOGINTITLE);
        setLocationRelativeTo(null);

        setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource(IMAGEICONLOC)));

        checkRememberMe();
    }

    /**
     * This method creates the main containers of the Login GUI.
     * There is a panel pnlContent as main panel. Then our 3 sub panels:
     *  Panel for input, panel for Logo at top and panel for 2 buttons below.
     */
    private void initMainComponents() {

        JPanel pnlContent = new JPanel(new BorderLayout());
        setContentPane(pnlContent);

        JPanel pnlLogo = new JPanel();
        pnlLogo.setLayout(new BorderLayout());

        JPanel pnlInput = new JPanel(new GridLayout(3, 3));
        pnlInput.setBorder(new EmptyBorder(10, 10, 10, 10));

        JPanel pnlButtons = new JPanel();
        pnlButtons.setLayout(new BorderLayout());

        pnlContent.add(pnlLogo, BorderLayout.NORTH);
        pnlContent.add(pnlInput, BorderLayout.CENTER);
        pnlContent.add(pnlButtons, BorderLayout.SOUTH);

        loadImage(pnlLogo);
        loadInput(pnlInput);
        loadButtons(pnlButtons);
    }

    /**
     * This method loads the "remember me" function if the xmlfile exists where
     * we keep the data.
     * 
     */
    private void checkRememberMe() {

        File xmlWereld = new File(XMLREMEMBERME);
        if (xmlWereld.exists()) {
            try {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document document = builder.parse(XMLREMEMBERME);

                NodeList nlUsernames = document.getElementsByTagName("username");
                Node nUsername = nlUsernames.item(0).getFirstChild();
                txtUser.setText(nUsername.getNodeValue());

                NodeList nlWorlds = document.getElementsByTagName("world");
                Node nWorld = nlWorlds.item(0).getFirstChild();

                for (int worldCount = 0; worldCount < cboWorld.getItemCount(); worldCount++) {
                    if (cboWorld.getItemAt(worldCount).toString().equals(nWorld.getNodeValue())) {
                        cboWorld.setSelectedIndex(worldCount);
                    }
                }
                // if file exist -> remember function in action
                chbRememberMe.setSelected(true);
                pwfPassword.requestFocus();

            } catch (SAXException ex) {
                Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ParserConfigurationException ex) {
                Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * There are 3 main parts of the gui and this methode loads the image on 
     * top of the login GUI. The image is placed as a ImageIcon on a label.
     * 
     * @param pnlLogo
     */
    private void loadImage(JPanel pnlLogo) {

        java.net.URL imageURL = Login.class.getResource(LOGINLOGOLOC);
        ImageIcon logo = new ImageIcon(imageURL);

        if (logo != null) {
            JLabel logoLabel = new JLabel(logo);
            pnlLogo.add(logoLabel, BorderLayout.NORTH);
        }
        JSeparator spSeparator = new JSeparator();
        pnlLogo.add(spSeparator, BorderLayout.SOUTH);
    }

    /**
     * There are 3 main parts of the gui and this methode load all the input 
     * controls like textfield for username, passwordfield, list with worlds,
     * "remember me" checkbox and the register button.
     * 
     * @param pnlInvoer
     */
    private void loadInput(JPanel pnlInvoer) {

        JLabel lblWorld = new JLabel("World:");
        lblWorld.setBorder(new EmptyBorder(0, 15, 0, 0));

        JLabel lblUser = new JLabel("Username:");
        lblUser.setBorder(new EmptyBorder(0, 15, 0, 0));
        JLabel lblPassword = new JLabel("Password:");
        lblPassword.setBorder(new EmptyBorder(0, 15, 0, 0));

        JPanel pnlWorld = new JPanel();
        pnlWorld.setLayout(new BoxLayout(pnlWorld, BoxLayout.X_AXIS));
        pnlWorld.setBorder(new EmptyBorder(5, 0, 5, 0));
        cboWorld = new JComboBox();
        pnlWorld.add(cboWorld);

        JPanel pnlRegister = new JPanel();
        pnlRegister.setLayout(new BoxLayout(pnlRegister, BoxLayout.X_AXIS));
        pnlRegister.setBorder(new EmptyBorder(4, 15, 4, 0));
        btnRegister = new JButton("Register");

        pnlRegister.add(btnRegister);

        LoadServersXMLInternet();

        JPanel pnlUser = new JPanel();
        pnlUser.setLayout(new BoxLayout(pnlUser, BoxLayout.X_AXIS));
        pnlUser.setBorder(new EmptyBorder(5, 0, 5, 0));
        txtUser = new JTextField();

        PlainDocument model = (PlainDocument) txtUser.getDocument();

        model.setDocumentFilter(new MyDocumentFilter(20));

        txtUser.addFocusListener(new FocusListener() {

            public void focusGained(FocusEvent e) {
                txtUser.selectAll();
            }

            public void focusLost(FocusEvent e) {
            }
        });
        pnlUser.add(txtUser);

        JPanel pnlPassword = new JPanel();
        pnlPassword.setLayout(new BoxLayout(pnlPassword, BoxLayout.X_AXIS));
        pnlPassword.setBorder(new EmptyBorder(5, 0, 5, 0));
        pwfPassword = new JPasswordField();

        pwfPassword.addFocusListener(new FocusListener() {

            public void focusGained(FocusEvent e) {
                pwfPassword.selectAll();
            }

            public void focusLost(FocusEvent e) {
            }
        });
        pwfPassword.addKeyListener(new KeyListener() {

            public void keyTyped(KeyEvent e) {
                if (e.getKeyChar() == '\n') {
                    loginMethod(txtUser.getText(), pwfPassword.getPassword());
                }
            }

            public void keyPressed(KeyEvent e) {
            }

            public void keyReleased(KeyEvent e) {
            }
        });
        PlainDocument modelPassword = (PlainDocument) pwfPassword.getDocument();

        modelPassword.setDocumentFilter(new MyDocumentFilter(20));

        pnlPassword.add(pwfPassword);

        JPanel pnlRememberMe = new JPanel();
        pnlRememberMe.setLayout(new BoxLayout(pnlRememberMe, BoxLayout.X_AXIS));
        pnlRememberMe.setBorder(new EmptyBorder(2, 12, 2, 10));
        chbRememberMe = new JCheckBox("Remember me");
        pnlRememberMe.add(chbRememberMe);

        pnlInvoer.add(lblWorld);
        pnlInvoer.add(pnlWorld);
        pnlInvoer.add(pnlRegister);
        pnlInvoer.add(lblUser);
        pnlInvoer.add(pnlUser);
        pnlInvoer.add(pnlRememberMe);
        pnlInvoer.add(lblPassword);
        pnlInvoer.add(pnlPassword);

        btnRegister.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                register();
            }
        });

    }

    /**
     * We use a public xmlfile from the internet for loading the world with their
     * Ip addresses and port numbers. This method uses the document 
     * from the "getDocument" method and uses it to fill the combobox 
     * with "XmlWorld" objects.
     */
    private void LoadServersXMLInternet() {
        try {

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = getDocument(db, XMLWERELDENLOC);
            if (doc != null) {
                doc.getDocumentElement().normalize();

                NodeList nodeLst = doc.getElementsByTagName("server");
                for (int s = 0; s < nodeLst.getLength(); s++) {
                    Node fstNode = nodeLst.item(s);
                    if (fstNode.getNodeType() == Node.ELEMENT_NODE) {

                        XmlServer XmlWorld = new XmlServer();
                        Element serverElement = (Element) fstNode;
                        NodeList nameLst = serverElement.getElementsByTagName("name");
                        Element nameElement = (Element) nameLst.item(0);
                        NodeList nameNL = nameElement.getChildNodes();

                        XmlWorld.setName(((Node) nameNL.item(0)).getNodeValue());

                        NodeList ipaddressLst = serverElement.getElementsByTagName("ipaddress");
                        Element ipaddressElement = (Element) ipaddressLst.item(0);
                        NodeList ipaddressNL = ipaddressElement.getChildNodes();

                        XmlWorld.setIpaddress(((Node) ipaddressNL.item(0)).getNodeValue());

                        NodeList portLst = serverElement.getElementsByTagName("port");
                        Element portElement = (Element) portLst.item(0);
                        NodeList portNL = portElement.getChildNodes();

                        XmlWorld.setPort(Integer.parseInt(((Node) portNL.item(0)).getNodeValue()));
                        cboWorld.addItem(XmlWorld);
                    }

                }

            } else {
                XmlServer XmlWorld = new XmlServer();
                XmlWorld.setName("locale");
                XmlWorld.setIpaddress("127.0.0.1");
                XmlWorld.setPort(2008);
                cboWorld.addItem(XmlWorld);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * We use a public xmlfile from the internet for loading the world with their
     * Ip addresses and port numbers. This method makes the connection and gets the
     * document of the xmlfile and returns it. 
     *  
     * @param db
     * @param urlString
     * @return
     */
    private static Document getDocument(DocumentBuilder db, String urlString) {
        Document doc = null;
        try {
            URL url = new URL(urlString);

            try {
                HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
                httpConnection.setConnectTimeout(500);

                int responseCode = httpConnection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    httpConnection.setConnectTimeout(50);
                    InputStream in = httpConnection.getInputStream();
                    try {
                        doc = db.parse(in);

                    } catch (org.xml.sax.SAXException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("HTTP connection response !=HTTP_OK");

                }

            } catch (java.net.SocketTimeoutException e) {
            // Cannot make connection to website doc will be null

            } catch (IOException e) {
            //e.printStackTrace();
            }
        } catch (MalformedURLException e) {
        //e.printStackTrace();
        } finally {
            return doc;
        }
    }

    /**
     * There are 3 main parts of the gui and this methode load the buttons 
     * (login and cancel button) with their actions if clicked. 
     * @param pnlButtons
     */
    private void loadButtons(JPanel pnlButtons) {

        Box ButtonBox = new Box(BoxLayout.X_AXIS);

        JSeparator sSeparator = new JSeparator();

        pnlButtons.add(sSeparator, BorderLayout.NORTH);
        pnlButtons.add(ButtonBox, BorderLayout.CENTER);

        btnLogin = new JButton("Login");
        JButton btnCancel = new JButton("Cancel");

        ButtonBox.add(Box.createHorizontalStrut(145));
        ButtonBox.add(btnLogin);
        ButtonBox.add(Box.createHorizontalStrut(15));
        ButtonBox.add(btnCancel);


        ButtonBox.setBorder(new EmptyBorder(5, 5, 10, 0));
        btnLogin.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                if (txtUser.getText().trim().equals("") || pwfPassword.getPassword().length == 0) {

                    JOptionPane.showMessageDialog(getLoginFrame(), "Please fill in username and password." +
                            "No username or password", "Error login", JOptionPane.WARNING_MESSAGE);

                } else {
                    setRememberMe();
                    loginMethod(txtUser.getText(), pwfPassword.getPassword());


                }

            }
        });

        btnCancel.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeLoginFrame();
            }
        });
    }

    /**
     * if user has selected the checkbox "remember me", it will make a new xmlfile
     * with the username and server he choose to remember. If the checkbox isn't 
     * selected we will delete the old xmlFile.
     */
    private void setRememberMe() {

        // in every case: delete old file 
        File xmlRememberMeOud = new File(XMLREMEMBERME);
        if (xmlRememberMeOud.exists()) {
            xmlRememberMeOud.delete();
        }
        if (chbRememberMe.isSelected()) {
            try {

                File xmlRememberMe = new File(XMLREMEMBERME);
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document document = builder.newDocument();

                Element nRoot = document.createElement("remember");

                Element nUserName = document.createElement("username");
                nUserName.appendChild(document.createTextNode(txtUser.getText()));

                Element nWorld = document.createElement("world");
                nWorld.appendChild(document.createTextNode(cboWorld.getSelectedItem().toString()));

                document.appendChild(nRoot);
                nRoot.appendChild(nUserName);
                nRoot.appendChild(nWorld);

                Transformer xformer = TransformerFactory.newInstance().newTransformer();
                DOMSource source = new DOMSource(document);
                StreamResult result = new StreamResult(xmlRememberMe);
                xformer.transform(source, result);

            } catch (ParserConfigurationException ex) {
                Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
            } catch (TransformerException ex) {
                Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    /**
     * Login the user in the game if he has entered a correct username and password. 
     * If he has a villagelocation already he will login directly to MaiaWorld. 
     * Otherwise he will have to pick a spot first.
     */
    private void loginMethod(final String sUsername, final char[] cPassword) {

        disableEnableControls(false);

        Thread t = new Thread() {

            @Override
            public void run() {

                // create client application
                XmlServer selectedServer = (XmlServer) cboWorld.getSelectedItem();
                Maia maia = new Maia(selectedServer.getIpadres(), selectedServer.getPoort());
                try {

                    // Make connection
                    maia.getMaiaClient().connect();

                    // see if login (username/password) is correct
                    if (maia.loginUser(sUsername, cPassword)) {
                        // Check if user has village already
                        if (maia.checkIfUserHasVillage(txtUser.getText())) {
                            maia.getMaiaClient().closeConnection();
                            // has a village
                            MaiaWorld frmMaiaWorld = new MaiaWorld(maia);
                            frmMaiaWorld.setVisible(true);
                        } else {
                            maia.getMaiaClient().closeConnection();
                            //has no village
                            ChooseVillage frmChooseVillage = new ChooseVillage(maia);
                            frmChooseVillage.setVisible(true);
                        }
                        getLoginFrame().dispose();

                    } else {
                        maia.getMaiaClient().closeConnection();
                        JOptionPane.showMessageDialog(getLoginFrame(), "An error occurred when trying to login. " +
                                "Please try again.", "Error login", JOptionPane.WARNING_MESSAGE);
                    }
                } catch (IOException ex) {
                    JOptionPane.showMessageDialog(getLoginFrame(), "The World of Maia cannot be reached at the moment." +
                            "Please try again later.", "No connection ", JOptionPane.WARNING_MESSAGE);
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
                } finally {

                    disableEnableControls(true);
                }
            }
        };
        t.start();
    }

    /**
     * This method will close the Login Frame
     */
    private void closeLoginFrame() {

        this.dispose();
        System.exit(0);

    }

    /**
     * Register a new account to a server trough the wizard. Depending on the
     * returncode of the wizard, it will add a user and a village to the database.
     */
    private void register() {

        Wizard wizard = new Wizard(this);
        wizard.getDialog().setTitle("Maia - Veni vidi vici ");

        WizardPanelDescriptor welcomeDspt = new WelcomePanelDescriptor();
        wizard.registerWizardPanel(WelcomePanelDescriptor.IDENTIFIER, welcomeDspt);

        userDataDspt = new UserDataPanelDescriptor();
        wizard.registerWizardPanel(UserDataPanelDescriptor.IDENTIFIER, userDataDspt);

        setMaiaDataDspt(new MaiaDataDescriptor());
        wizard.registerWizardPanel(MaiaDataDescriptor.IDENTIFIER, getMaiaDataDspt());

        WizardPanelDescriptor checkDataDspt = new CheckDataPanelDescriptor((XmlServer) cboWorld.getSelectedItem(),
                (UserDataPanel) userDataDspt.getPanelComponent(), (MaiaDataPanel) getMaiaDataDspt().getPanelComponent());
        wizard.registerWizardPanel(CheckDataPanelDescriptor.IDENTIFIER, checkDataDspt);

        wizard.setCurrentPanel(WelcomePanelDescriptor.IDENTIFIER);

        int ret = wizard.showModalDialog();
        if (ret == 0) {

            // Disable all controls : enable=false
            disableEnableControls(false);

            Thread t = new Thread() {

                public void run() {

                    try {
                        // Make connection
                        XmlServer selectedServer = (XmlServer) cboWorld.getSelectedItem();
                        Maia maia = new Maia(selectedServer.getIpadres(), selectedServer.getPoort());
                        // CONNECT
                        maia.getMaiaClient().connect();

                        String sName = ((UserDataPanel) getUserDataDspt().getPanelComponent()).getTxtName().getText();
                        String sFirstname = ((UserDataPanel) getUserDataDspt().getPanelComponent()).getTxtFirstName().getText();
                        String sUsername = ((UserDataPanel) getUserDataDspt().getPanelComponent()).getTxtUsername().getText();
                        char[] cPassword = ((UserDataPanel) getUserDataDspt().getPanelComponent()).getPwfPassword().getPassword();
                        String sEmail = ((UserDataPanel) getUserDataDspt().getPanelComponent()).getTxtEmail().getText();
                        String sCountry = ((UserDataPanel) getUserDataDspt().getPanelComponent()).getCboCountry().getSelectedItem().toString();

                        maia.registerUser(sName, sFirstname, sUsername, cPassword, sEmail, sCountry);

                        //After finishing wizard we need to create a village for a user with location = -1 and with userId

                        // so we need to get userId from new user                      
                        User user = maia.getUserByName(sUsername);

                        String sVillageName = ((MaiaDataPanel) getMaiaDataDspt().getPanelComponent()).getTxtVillage().getText();
                        // create village 
                        maia.createNewDummyVillage(sVillageName, user.getUserId());

                        // CLOSE CONNECTION
                        maia.getMaiaClient().closeConnection();

                        // Enables all controls : enable=true
                        disableEnableControls(true);

                    } catch (IOException ex) {
                        Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
                        JOptionPane.showMessageDialog(getLoginFrame(), "The World of Maia cannot be reached at the moment. " +
                                "Please try again later.", "No connection ", JOptionPane.WARNING_MESSAGE);
                    } catch (ClassNotFoundException ex) {
                        Logger.getLogger(Login.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                };
            t.start();
        }
    }

    public void disableEnableControls(boolean enable) {

        btnLogin.setEnabled(enable);
        cboWorld.setEnabled(enable);
        txtUser.setEnabled(enable);
        pwfPassword.setEnabled(enable);
        chbRememberMe.setEnabled(enable);
        btnRegister.setEnabled(enable);
    }

    // <editor-fold defaultstate="collapsed" desc="Getters/Setters">                     
    public Login getLoginFrame() {
        return this;
    }

    public WizardPanelDescriptor getUserDataDspt() {
        return userDataDspt;
    }

    public void setUserDataDspt(WizardPanelDescriptor userDataDspt) {
        this.userDataDspt = userDataDspt;
    }

    public WizardPanelDescriptor getMaiaDataDspt() {
        return maiaDataDspt;
    }

    public void setMaiaDataDspt(WizardPanelDescriptor maiaDataDspt) {
        this.maiaDataDspt = maiaDataDspt;
    }
    //</editor-fold>  
    /**
     * A class wich sets a limited number of characters to a document.
     * We can use this document then for a textfield.
     */
    private static class MyDocumentFilter extends DocumentFilter {

        private int size;

        public MyDocumentFilter(int size) {
            this.size = size;
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (fb.getDocument().getLength() + text.length() > size) {
                return;
            }

            super.replace(fb, offset, length, text, attrs);
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The pnlContent of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new Login().setVisible(true);


            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
}
